package org.devio.takephoto.compress;

import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import org.devio.takephoto.ResourceTable;
import org.devio.takephoto.uitl.ResUtil;
import org.devio.takephoto.uitl.TFileUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.IOException;

/**
 *
 * @author JPH
 *         Date 2015-08-26 1:44:26
 *         Version:1.0.3
 */
class CompressImageUtil {
    private static final String TAG = CompressImageUtil.class.getSimpleName();
    private CompressConfig config;
    private Context context;
    private EventHandler mhHandler = new EventHandler(EventRunner.create());

    CompressImageUtil(Context context, CompressConfig config) {
        this.context = context;
        this.config = config == null ? CompressConfig.ofDefaultConfig() : config;
    }

    /**
     * This method id used to compress image
     *
     * @param imagePath Path of taken image from camera
     * @param listener  listener to handle compression results
     */
    void compress(String imagePath, CompressListener listener) {
        if (config.isEnablePixelCompress()) {
            try {
                compressImageByPixel(imagePath, listener);
            } catch (FileNotFoundException e) {
                listener.onCompressFailed(imagePath, String.format(ResUtil.getString(context, ResourceTable.String_failed_to_compress_image), e.toString()));
                e.printStackTrace();
            }
        } else {
            ImageSource newOpts = ImageSource.create(imagePath, new ImageSource.SourceOptions());
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            PixelMap bitmap = newOpts.createPixelmap(decodingOptions);
            compressImageByQuality(bitmap, imagePath, listener);
        }
    }

    /**
     * This method is used to compress image by quality
     *
     * @param bitmap   Bitmap created by imagepacker after taking picture from camera
     * @param imgPath  path of you taken picture
     * @param listener listener to handle compression results
     */
    private void compressImageByQuality(final PixelMap bitmap, final String imgPath, final CompressListener listener) {
        if (bitmap == null) {
            sendMsg(false, imgPath, ResUtil.getString(context, ResourceTable.String_failed_pixel_compress), listener);
            return;
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                int options = 100;
                OutputStream outputStream = null;
                ByteArrayOutputStream baos = null;
                try {
                    baos = new ByteArrayOutputStream();
                    outputStream = new FileOutputStream(new File(imgPath));
                    ImagePacker imagePacker = ImagePacker.create();
                    ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
                    packingOptions.format = "image/jpeg";
                    packingOptions.quality = options;
                    packingOptions.numberHint = 1;
                    imagePacker.initializePacking(outputStream, packingOptions);
                    imagePacker.addImage(bitmap);
                    imagePacker.finalizePacking();
                    imagePacker.release();
                    outputStream.write(baos.toByteArray());
                    outputStream.close();

                    while (baos.toByteArray().length > config.getMaxSize()) {
                        baos.reset();
                        options -= 5;
                        if (options <= 5) {
                            options = 5;
                        }
                        if (options == 5) {
                            break;
                        }
                    }

//                    outputStream = new FileOutputStream(new File(imgPath));
                    ImagePacker imagePacker2 = ImagePacker.create();
                    ImagePacker.PackingOptions packingOptions2 = new ImagePacker.PackingOptions();
                    packingOptions2.format = "image/jpeg";
                    packingOptions2.quality = options;
                    packingOptions2.numberHint = 1;
                    imagePacker2.initializePacking(baos, packingOptions);
                    imagePacker2.addImage(bitmap);
                    imagePacker2.finalizePacking();
                    imagePacker2.release();

                    File thumbnailFile = getThumbnailFile(new File(imgPath));
                    FileOutputStream fos = new FileOutputStream(thumbnailFile);
                    fos.write(baos.toByteArray());
                    fos.flush();
                    fos.close();
                    sendMsg(true, thumbnailFile.getPath(), null, listener);
                } catch (IOException e) {
                } finally {
                    if (outputStream != null) {
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            // Do nothing.
                        }
                    }
                }
            }
        }).start();
    }

    /**
     * This method is used to compress image by pixels
     *
     * @param imgPath  Path of your image taken by camera
     * @param listener listener to handle compression results
     * @throws FileNotFoundException
     */
    private void compressImageByPixel(String imgPath, CompressListener listener) throws FileNotFoundException {
        if (imgPath == null) {
            sendMsg(false, imgPath, ResUtil.getString(context, ResourceTable.String_file_doesnot_exist), listener);
            return;
        }
        ImageSource newOpts = ImageSource.create(imgPath, new ImageSource.SourceOptions());
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        int width = newOpts.getImageInfo().size.width;
        int height = newOpts.getImageInfo().size.height;
        float maxSize = config.getMaxPixel();
        int be = 1;
        if (width >= height && width > maxSize) {
            be = (int) (newOpts.getImageInfo().size.width / maxSize);
            be++;
        } else if (width < height && height > maxSize) {
            be = (int) (newOpts.getImageInfo().size.height / maxSize);
            be++;
        }
        decodingOptions.sampleSize = be;
        decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
        PixelMap bitmap = newOpts.createPixelmap(decodingOptions);
        if (config.isEnableQualityCompress()) {
            compressImageByQuality(bitmap, imgPath, listener);
        } else {
            File thumbnailFile = getThumbnailFile(new File(imgPath));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
            packingOptions.format = "image/jpeg";
            packingOptions.quality = 100;
            imagePacker.initializePacking(baos, packingOptions);
            if (bitmap != null) {
                imagePacker.addImage(bitmap);
                bitmap.release();
            }
            imagePacker.finalizePacking();
            imagePacker.release();
            listener.onCompressSuccess(thumbnailFile.getPath());
        }
    }

    /**
     * This method is used to send messahe based on compression result
     *
     * @param isSuccess flag to check compression is successful or not
     * @param imagePath path of your taken image
     * @param message   message in string which we want to show
     * @param listener
     */
    private void sendMsg(final boolean isSuccess, final String imagePath, final String message, final CompressListener listener) {
        mhHandler.postTask(new Runnable() {
            @Override
            public void run() {
                if (isSuccess) {
                    listener.onCompressSuccess(imagePath);
                } else {
                    listener.onCompressFailed(imagePath, message);
                }
            }
        });
    }

    /**
     * This method is used to get thumbnail of image file
     *
     * @param file file from given image path
     * @return file
     */
    private File getThumbnailFile(File file) {
        if (file == null || !file.exists()) {
            return file;
        }
        return TFileUtils.getPhotoCacheDir(context, file);
    }

    public interface CompressListener {
        void onCompressSuccess(String imgPath);

        void onCompressFailed(String imgPath, String msg);
    }
}
